/**
 * 默认的 z-index 初值
 *
 * @type {number}
 */
let z_index = 10241024;
// let z_index = new Date().getTime() & 0xffff;

/**
 * 可复用的吸附窗口
 *
 * 基本原理，就是在 body 追加一个 dom 组件，在使用的时候，拽到所需的位置。
 *
 * 注意：
 * 1、dom 组件只是放在屏幕外面，而不是隐藏的。
 * 2、dom 对象的 append() 和 remove() 操作需要手动执行。
 */
class Popover {

    constructor() {
        /**
         * 定位组件
         *
         * 弹窗展示在什么位置，根据组件的位置进行定位。
         *
         * @type {HTMLElement}
         * @private
         */
        this.ref_ = undefined;
        /**
         * 弹窗本身
         *
         * @type {HTMLElement}
         * @private
         */
        this.dom_ = undefined;
    }

    /**
     * 设置 z-index 初值
     *
     * @param i
     */
    static setStartZIndex(i){
        z_index = i;
    }

    /**
     * 定位组件
     *
     * @param r {HTMLElement}
     */
    set ref(r) {
        this.ref_ = r;
    }

    /**
     * 定位组件
     */
    get ref() {
        return this.ref_;
    }

    /**
     * 弹窗内容
     *
     * @param d {HTMLElement}
     */
    set dom(d) {
        this.dom_ = d;
    }


    /**
     * 弹窗内容
     */
    get dom() {
        return this.dom_;
    }

    /**
     * 设置 index
     *
     * @param index
     */
    setZIndex(index) {
        this.dom_.style.zIndex = index;
    }

    /**
     * 使用内置的 z-index
     */
    withZIndex() {
        z_index++;
        this.dom_.style.zIndex = z_index;
    }


    /**
     * 设置 z-index，使之能覆盖提供的 dom 对象。
     *
     * @param d {HTMLElement}   用于比较的 dom 对象
     * @param def {number}      默认的 z-index
     */
    cover(d, def = undefined) {
        let index = DocumentUtils.getZIndex(d);
        if (index === -1) {
            if (def === undefined) {
                // 使用时间戳作为 z-index
                z_index++;
                index = z_index;
            } else {
                // 使用指定的默认值
                index = def;
            }
        } else {
            // 尝试从提供的 dom 组件
            index++;
        }
        this.dom_.style.zIndex = index;
    }

    /**
     * 隐藏组件
     *
     * 实际只是放到了屏幕之外
     */
    hidden() {
        this.dom_.style.left = '-1000px';
        this.dom_.style.top = '-1000px';
    }

    /**
     * 将 dom 组件展示在指定的位置
     *
     * @param left  相对于屏幕的宽度
     * @param top   相对于屏幕的高度
     */
    open(left, top) {
        this.dom_.style.left = left + 'px';
        this.dom_.style.top = top + 'px';
    }

    /**
     * 将 dom 组件展示在指定的位置
     *
     * 自动微调属性，始终保证 dom 不会展示在容器外面。
     *
     * @param left  相对于 ref 的 left
     * @param top   相对于 ref 的 top
     */
    openInBox(left, top) {
        const rect = this.ref_.getBoundingClientRect();
        const dom = this.dom_;

        // 相对于屏幕的 left/top
        let l = left + rect.left;
        let t = top + rect.top;

        // top 不足以放下 dom 组件
        if (rect.bottom - t < dom.clientHeight) {
            t -= dom.clientHeight;
        }

        // left 不足以放下 dom 组件
        if (rect.left + rect.width - l < 100) {
            l -= dom.clientWidth;
        }

        this.open(l, t);
    }

    /**
     * 根据组件的相对位置
     *
     * 将弹窗放置在组件的外面。
     @param direction {string}      弹窗方向
     @param align {string}          对齐方式
     @param margin {number}         预留外边框
     */
    openOf(direction, align, margin = 0) {
        const rect = this.ref_.getBoundingClientRect();
        const slf = this.dom_.getBoundingClientRect();

        // 相对于屏幕的 left/top
        let l = 0;
        let t = 0;

        if (direction === 'bottom') {
            t = rect.bottom + margin;
        } else if (direction === 'top') {
            t = rect.top - slf.height - margin;
        } else if (direction === 'left') {
            l = rect.left - slf.width - margin;
        } else if (direction === 'right') {
            l = rect.left + rect.width + margin;
        }

        if (direction === 'bottom' || direction === 'top') {
            if (align === 'start') {
                l = rect.left;
            } else if (align === 'end') {
                l = rect.left + rect.width - slf.width;
            } else {
                l = rect.left + rect.width / 2 - slf.width / 2;
            }
        } else {
            if (align === 'start') {
                t = rect.top;
            } else if (align === 'end') {
                t = rect.bottom - slf.height;
            } else {
                t = rect.top + rect.height / 2 - slf.height / 2;
            }
        }

        this.open(l, t);
    }


    /**
     * 根据组件的相对位置
     *
     * 将弹窗放置在组件的内部。
     *
     * @param direction {string}    弹窗方向
     * @param align {string}        对齐方式
     * @param padding {number}      预留内边框
     */
    openIn(direction, align, padding = 0) {
        const rect = this.ref_.getBoundingClientRect();
        const slf = this.dom_.getBoundingClientRect();

        // 相对于屏幕的 left/top
        let l = 0;
        let t = 0;

        if (direction === 'bottom') {
            t = rect.bottom - slf.height - padding;
        } else if (direction === 'top') {
            t = rect.top + padding;
        } else if (direction === 'left') {
            l = rect.left + padding;
        } else if (direction === 'right') {
            l = rect.left + rect.width - slf.width - padding;
        }

        if (direction === 'bottom' || direction === 'top') {
            if (align === 'start') {
                l = rect.left + padding;
            } else if (align === 'end') {
                l = rect.left + rect.width - slf.width - padding;
            } else {
                l = rect.left + rect.width / 2 - slf.width / 2;
            }
        } else {
            if (align === 'start') {
                t = rect.top + padding;
            } else if (align === 'end') {
                t = rect.bottom - slf.height - padding;
            } else {
                t = rect.top + rect.height / 2 - slf.height / 2;
            }
        }

        this.open(l, t);
    }

    /**
     * 关闭选项卡右击菜单
     *
     * 下拉列表展开之后，不论点击哪里，都应该关闭弹窗。
     * 如果点击的是弹窗里面的东西，就不应该关闭。
     */
    onWindowsMousedown(evt) {
        if (!this.dom_.contains(evt.target)) {
            this.hidden();
        }
    }
}

export default Popover;


